﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// applies a Perona-Malik diffusion filter to .
    /// </summary>
    public class PeronaMalikFilter : BaseImageFilter
    {
        protected static double lambda = 0.25;
        protected bool m_bColor = true;
        protected int m_nK = 1;
        protected int m_nIterations = 10;
        /// <summary>
        /// 
        /// </summary>
        public PeronaMalikFilter()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_peronaMalikFilter"></param>
        internal PeronaMalikFilter(PeronaMalikFilter _peronaMalikFilter)
        {
        }

        /// <summary>
        /// Segments this pic using anisotropic gray
        /// </summary>
        /// <param name="imname"></param>
        /// <param name="K"></param>
        /// <param name="totalIterations"></param>
        /// <returns></returns>
        //public static IntMatrix SegmentAnisotropicGray(string imname, int K, int totalIterations)
        //{
        //    // Anisotropic diffusion for a gray-scale (single channel) Image.

        //    // Color image will be automatically converted to brightness...
        //    RGBIntMatrix imGrayOriginal = RGBIntMatrix.ReadImage(imname);
        //    DoubleMatrix imGray = imGrayOriginal.Intensity();
        //    // Update the image in place
        //    Anisotropic.AnisoDiff(imGray, K, totalIterations);

        //    imGray.WriteImageScaled(String.Format("GrayDiffused.{0}.png", K));


        //    ComplexDoubleMatrix gdGrad = Tools.Gradient(imGray);
        //    DoubleMatrix f = DoubleMatrix.VectorialAdd(gdGrad);
        //    DoubleMatrix ctemp = f.Clone();
        //    DoubleMatrix f3 = Tools.NonMaximaSuppress(ctemp, gdGrad);

        //    DoubleMatrix t = f.Clone();
        //    f.Threshold(K);

        //    IntMatrix labels = Tools.GetEgdes(t, (IntMatrix)f, (IntMatrix)f3);
        //    RGBIntMatrix save = imGrayOriginal.Clone();
        //    save.R.MultiplyValue(200);
        //    save.R.DivideValue(255);
        //    save.G.MultiplyValue(200);
        //    save.G.DivideValue(255);
        //    save.B.MultiplyValue(200);
        //    save.B.DivideValue(255);

        //    DoubleMatrix saveInten = save.Intensity();
        //    IntMatrix curves = new IntMatrix(imGray.XDim, imGray.YDim, 255);

        //    for (int x = 0; x < imGray.XDim; x++)
        //    {
        //        for (int y = 0; y < imGray.YDim; y++)
        //        {
        //            if (labels.data[x, y] == 1)
        //            {
        //                save.r.data[x, y] = 255;
        //                save.g.data[x, y] = 255;
        //                save.b.data[x, y] = 255;
        //                saveInten.data[x, y] = 255;
        //                curves.data[x, y] = 0;
        //            }
        //        }
        //    }
        //    save.WriteImage(String.Format("GraySegmented.{0}.png", K));
        //    saveInten.WriteImage(String.Format("GraySegmented2.{0}.png", K));
        //    curves.WriteImage(String.Format("GrayCurves.{0}.png", K));

        //    return curves;
        //}

        /// <summary>
        /// Segments this picture using anisotropic color diffusion
        /// </summary>
        /// <param name="imname"></param>
        /// <param name="K"></param>
        /// <param name="totalIterations"></param>
        /// <returns></returns>
        //public static IntMatrix SegmentAnisotropicColor(string imname, int K, int totalIterations)
        //{
        //    // Anisotropic diffusion for a color 
        //    // (multi channel - can be of more than 3 dimensions) Image.

        //    RGBIntMatrix imOriginal = RGBIntMatrix.ReadImage(imname);
        //    RGBDoubleMatrix im = (RGBDoubleMatrix)imOriginal.Clone();
        //    // Convert to Lab color space.
        //    im = ColorSpaces.RGB2Lab(im);
        //    // Update the image in place (this time in color space)
        //    Anisotropic.AnisoDiff((MultiValuedDoubleMatrix)im, K, totalIterations);

        //    ColorSpaces.Lab2RGB(im).WriteImageScaled(String.Format("ColorDiffused.{0}.png", K));

        //    im.ImageScale();
        //    ComplexDoubleMatrix gdGradColor = Tools.GradientMulti(im);
        //    DoubleMatrix fColor = DoubleMatrix.VectorialAdd(gdGradColor);
        //    DoubleMatrix ctempColor = fColor.Clone();
        //    DoubleMatrix f3Color = Tools.NonMaximaSuppress(ctempColor, gdGradColor);

        //    DoubleMatrix tColor = fColor.Clone();
        //    fColor.Threshold(K / 3d);

        //    IntMatrix labelsColor = Tools.GetEgdes(tColor, (IntMatrix)fColor, (IntMatrix)f3Color);
        //    RGBIntMatrix saveColor = imOriginal.Clone();
        //    saveColor.R.MultiplyValue(200);
        //    saveColor.R.DivideValue(255);
        //    saveColor.G.MultiplyValue(200);
        //    saveColor.G.DivideValue(255);
        //    saveColor.B.MultiplyValue(200);
        //    saveColor.B.DivideValue(255);

        //    IntMatrix curvesColor = new IntMatrix(im.XDim, im.YDim, 255);

        //    for (int x = 0; x < im.XDim; x++)
        //    {
        //        for (int y = 0; y < im.YDim; y++)
        //        {
        //            if (labelsColor.data[x, y] == 1)
        //            {
        //                saveColor.r.data[x, y] = 255;
        //                saveColor.g.data[x, y] = 255;
        //                saveColor.b.data[x, y] = 255;
        //                curvesColor.data[x, y] = 0;
        //            }
        //        }
        //    }
        //    saveColor.WriteImage(String.Format("ColorSegmented.{0}.png", K));
        //    curvesColor.WriteImage(String.Format("ColorCurves.{0}.png", K));

        //    return curvesColor;
        //}

        public static void AnisoDiff(VectorMatrix image, double K, int iterations)
        {
            for (int i = 0; i < iterations; i++)
            {
                AnisoDiff(image, K);
            }
        }

        public static void AnisoDiff(VectorMatrix image, double K)
        {
            /*ComplexDoubleMatrix grad = new ComplexDoubleMatrix(image.XDim, image.YDim, 0d);
            ComplexDoubleMatrix flux = new ComplexDoubleMatrix(image.XDim, image.YDim, 0d);

            // Calculate gradient
            for (int i = 1; i < image.XDim; i++)
            {
                for (int j = 0; j < image.YDim; j++)
                {
                    grad.real.data[i, j] = image.data[i - 1, j] - image.data[i, j];
                }
            }

            for (int i = 0; i < image.XDim; i++)
            {
                for (int j = 1; j < image.YDim; j++)
                {
                    grad.imag.data[i, j] = image.data[i, j - 1] - image.data[i, j];
                }
            }


            // Calculate flux
            for (int i = 0; i < image.XDim; i++)
            {
                for (int j = 0; j < image.YDim; j++)
                {
                    flux.real.data[i, j] = grad.real.data[i, j] * Math.Exp(-(1 / K) * Math.Abs(grad.real.data[i, j]));
                    flux.imag.data[i, j] = grad.imag.data[i, j] * Math.Exp(-(1 / K) * Math.Abs(grad.imag.data[i, j]));
                }
            }


            // Update image
            for (int i = 0; i < image.XDim - 1; i++)
            {
                for (int j = 0; j < image.YDim - 1; j++)
                {
                    image.data[i, j] += lambda * (flux.real.data[i, j] - flux.real.data[i + 1, j]
                        + flux.imag.data[i, j] - flux.imag.data[i, j + 1]);
                }
            }


            for (int j = 0; j < image.YDim - 1; j++)
            {
                image.data[image.XDim - 1, j] += lambda * (flux.imag.data[image.XDim - 1, j] - flux.imag.data[image.XDim - 1, j + 1]);
            }

            for (int i = 0; i < image.XDim - 1; i++)
            {
                image.data[i, image.YDim - 1] += lambda * (flux.imag.data[i, image.YDim - 1] - flux.imag.data[i + 1, image.YDim - 1]);
            }*/
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            //if (m_bColor)
            //{
            //    _bitmap = SegmentAnisotropicColor(_bitmap, m_nK, m_nIterations);
            //}
            //else
            //{
            //    _bitmap = SegmentAnisotropicGray(_bitmap, m_nK, m_nIterations);
            //}
            return _bitmap;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new PeronaMalikFilter(this);
        }
    }
}
